package cn.hloger.spider.demo.mathDemo;

import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
import org.apache.commons.math3.fitting.PolynomialCurveFitter;
import org.apache.commons.math3.fitting.WeightedObservedPoints;

/**
 * 根据历史正确率  根据模型算未来的值
 * 这是个曲线运算模型,
 * 模型的数学阶数越多(默认为3阶),模型的历史数据越多, 预测的准确率越准确
 * 参考 :
 * 一阶模型 : y = ax+b
 * 二阶模型 : y = ax^2+bx+c
 * 三阶模型 : y = ax^3+bx^2+cx+d
 * .... 这块阶数 我写成了可配置的
 *
 * @author huanglong
 * @Description 根据历史正确率  根据模型算未来的值
 * @data 2022/9/12
 */
public class MathOrderModelService {

    public static final int DEFAULT_ORDER = 3;

    public static void main(String[] args) {
        // 已知的近10天做题正确率数据
        double[] days = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        double[] correctRates = {4, 9, 16, 25, 36, 49, 64, 81, 100, 121};
        int count = days.length;
        WeightedObservedPoints obs = new WeightedObservedPoints();
        for (int i = 0; i < days.length; i++) {
            obs.add(days[i], correctRates[i]);
        }
        //执行趋势图预测

        //第一种调用方式  参数 1. 坐标系  2,预测的x坐标系
        System.out.println("第一种调用方式 =============================");
        double[] forecastX = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20};
        double[] correctRate = getCorrectRate(obs, forecastX);
        for (int i = 0; i < correctRate.length; i++) {
            System.out.println("第 " + (count + i + 1) + " 天的预测正确率为: " + correctRate[i]);
        }
        System.out.println("=============================");

        //第二种调用方式  参数 1. 坐标系  2,之后预测多少个x坐标系
        System.out.println("第二种调用方式 =============================");
        double[] correctRate2 = getCorrectRate(obs, 10);
        for (int i = 0; i < correctRate2.length; i++) {
            System.out.println("第 " + (count + i + 1) + " 天的预测正确率为: " + correctRate[i]);
        }
    }


    /**
     * 使用函数趋势图分析数据
     *
     * @param order       阶数,默认为3阶,越高越精确
     * @param historyData 历史坐标系,historyData.add(x,y),x为答题次数, y为合格率
     * @param forecastX   需要预测的横坐标数据  ,这里是指的x坐标的值,也就是第几次
     * @return double[] 结果集
     * @author huanglong
     * @date 2024/5/13
     */
    public static double[] getCorrectRate(int order, WeightedObservedPoints historyData, double[] forecastX) {
        if (order == 0 || historyData == null || historyData.toList().size() == 0 || forecastX == null || forecastX.length == 0) {
            throw new IllegalArgumentException("参数错误");
        }
        // 使用 PolynomialCurveFitter 拟合三次多项式曲线
        PolynomialCurveFitter fitter = PolynomialCurveFitter.create(order);
        double[] fittedParams = fitter.fit(historyData.toList());

        // 使用得到的拟合系数创建多项式函数
        PolynomialFunction fittedFunction = new PolynomialFunction(fittedParams);
//        System.out.println("模型公式为:y=" + fittedFunction.toString());

        double[] result = new double[forecastX.length];
//        // 使用得到的拟合参数创建多项式函数
        // 预测接下来的正确率,这里是预测为 y=ax^2 +bx+c
        int flag = 0;
        for (int i = 0; i < forecastX.length; i++) {
            double predictedRate = fittedFunction.value(forecastX[i]);
            result[flag] = predictedRate;
            flag++;
        }
        return result;
    }

    public static double[] getCorrectRate(WeightedObservedPoints historyData, double[] forecastX) {
        return getCorrectRate(DEFAULT_ORDER, historyData, forecastX);
    }


    /**
     * 使用霍尔特-温特斯线性趋势模型分析数据,预测----默认3阶模型
     *
     * @param historyData   历史坐标系,historyData.add(x,y),x为答题次数, y为合格率
     * @param forecastCount 需要预测的次数,如预测历史数据之后2次,就是2
     * @return double[]
     * @author huanglong
     * @date 2024/5/13
     */
    public static double[] getCorrectRate(WeightedObservedPoints historyData, int forecastCount) {
        return getCorrectRate(DEFAULT_ORDER, historyData, forecastCount);
    }

    /**
     * 使用霍尔特-温特斯线性趋势模型分析数据,预测
     *
     * @param historyData   历史坐标系,historyData.add(x,y),x为答题次数, y为合格率
     * @param forecastCount 需要预测的次数,如预测历史数据之后2次,就是2
     * @return double[]
     * @author huanglong
     * @date 2024/5/13
     */
    public static double[] getCorrectRate(int order, WeightedObservedPoints historyData, int forecastCount) {
        int size = historyData.toList().size();
        double[] forecastX = new double[forecastCount];
        for (int i = 0; i < forecastCount; i++) {
            forecastX[i] = size + i + 1;
        }
        return getCorrectRate(order, historyData, forecastX);
    }
}
